# -*- coding: utf-8 -*-
# =============================================================================
#     FileName:
#         Desc:
#       Author: GGA
#        Email:
#     HomePage:
#      Version: 1.0.1
#   LastChange: 2020-12-20
#      History:
# =============================================================================
import datetime
import queue
import threading
import time

import fire
import psutil
import redis
from utils.human_helper import HumanHelper


class RedisMemoryInfo(object):
    def __init__(self, redis_host, redis_port, redis_auth):
        self.redis_host = redis_host
        self.redis_port = redis_port
        self.redis_auth = redis_auth
        self.redis_conn = redis.StrictRedis(
            host=self.redis_host, port=self.redis_port,
            password=self.redis_auth, db=0, socket_timeout=3
        )

    def _show_client_list_info(self):
        client_list = self.redis_conn.client_list()
        normal_list = list(filter(lambda item: item["flags"] != "S" and item["flags"] != "M", client_list))
        slave_list = list(filter(lambda item: item["flags"] == "S", client_list))
        master_list = list(filter(lambda item: item["flags"] == "M", client_list))
        normal_memory = HumanHelper.format_byte_number(sum(int(item["omem"]) for item in normal_list))
        slave_memory = HumanHelper.format_byte_number(sum(int(item["omem"]) for item in slave_list))
        master_memory = HumanHelper.format_byte_number(sum(int(item["omem"]) for item in slave_list))
        total_memory = HumanHelper.format_byte_number(sum(int(item["omem"]) for item in client_list))
        top_5_list = list(sorted(client_list, key=lambda item: int(item["omem"]), reverse=True))[0:5]
        print("=" * 50)
        print("客户端连接内存消耗：")
        print("\t当前有{}个客户端连接, 使用内存{}".format(len(client_list), total_memory))
        print("\t当前有{}个Master连接, 使用内存{}".format(len(master_list), master_memory))
        print("\t当前有{}个Slave连接, 使用内存{}".format(len(slave_list), slave_memory))
        print("\t当前有{}个普通连接, 使用内存{}".format(len(normal_list), normal_memory))
        print("客户端链接使用内存TOP5:")
        for item in top_5_list:
            print("\taddr:{}, memory:{}".format(item["addr"], HumanHelper.format_byte_number(int(item["omem"]))))

    def _show_used_memory_info(self):
        memory_info = self.redis_conn.info(section="memory")
        # print(memory_info)

        used_memory = memory_info["used_memory"]
        max_memory = memory_info["maxmemory"]
        used_memory_rss = memory_info["used_memory_rss"]
        used_memory_dataset = memory_info["used_memory_dataset"]
        used_memory_overhead = memory_info["used_memory_overhead"]
        used_memory_startup = memory_info["used_memory_startup"]
        used_memory_lua = memory_info["used_memory_lua"]
        used_memory_perc = str(round(used_memory * 100 / max_memory, 2)) + "%"
        used_memory_startup_human = HumanHelper.format_byte_number(used_memory_startup)
        used_memory_dataset_human = HumanHelper.format_byte_number(used_memory_dataset)
        used_memory_overhead_human = HumanHelper.format_byte_number(used_memory_overhead)
        mem_fragmentation_human = HumanHelper.format_byte_number(used_memory_rss - used_memory - used_memory_startup)
        used_memory_buffer_human = HumanHelper.format_byte_number(
            used_memory - used_memory_dataset - used_memory_overhead)
        print("=" * 50)
        print("Redis实例内存使用详情：")
        print("操作物理内存: {} (当前系统的物理内存)".format(memory_info["total_system_memory_human"]))
        print("设置最大内存: {} (设置Redis实例可使用的最大内存)".format(memory_info["maxmemory_human"]))
        print("实例分配内存: {} (操作系统为Redis实例分配的物理内存)".format(memory_info["used_memory_rss_human"]))
        print("实例使用内存: {} (使用内存分配器申请的内存,包含虚拟内存)".format(memory_info["used_memory_human"]))
        print("\t实例启动使用内存: {} (启动Redis实例消耗内存)".format(used_memory_startup_human))
        print("\t实例数据使用内存: {} (存储当前数据和数据结构使用的内存)".format(used_memory_dataset_human))
        print("\t实例开销使用内存: {} (包含客户端元数据管理消耗等内存)".format(used_memory_overhead_human))
        print("\t实例Lua使用内存: {} (Lua进程使用的物理内存)".format(memory_info["used_memory_lua_human"]))
        print("\t实例缓冲使用内存: {} (客户端缓冲、复制积压缓冲、AOF重写缓冲使用内存)".format(used_memory_buffer_human))
        print("实例内存使用率: {} (已使用内存与最大内存的比率)".format(used_memory_perc))
        print("实例内存碎片量: {} (实例内存碎片使用量)".format(mem_fragmentation_human))
        print("实例内存碎片率: {}% (实例分配内存与实例使用内存的比率)".format(memory_info["mem_fragmentation_ratio"]))
        print("实例内存分配器: {} (Redis实例使用的内存分配器)".format(memory_info["mem_allocator"]))
        print("最大内存策略: {} (已使用内存达到最大内存时使用的数据逐出策略)".format(memory_info["maxmemory_policy"]))

    def _show_db_key_info(self):
        db_list = self.redis_conn.info(section="Keyspace")
        total_keys = 0
        total_expires = 0
        total_ttl = 0
        for db_item in db_list.values():
            total_keys += db_item["keys"]
            total_expires += db_item["expires"]
            total_ttl = db_item["keys"] * db_item["avg_ttl"]
        avg_ttl = int(total_ttl / (total_keys + 1) / 1000)
        print("=" * 50)
        print("Redis实例Key使用详情：")
        print("\t实例Key总量: {}个".format(HumanHelper.format_int_number(total_keys)))
        print("\t实例过期Key总量: {}个".format(HumanHelper.format_int_number(total_expires)))
        print("\t实例平均过期时间: {}".format(HumanHelper.convert_second_number(avg_ttl)))

    def show_memory_info(self):
        self._show_used_memory_info()
        self._show_client_list_info()
        self._show_db_key_info()


def main(redis_host="127.0.0.1", redis_port=6379, redis_auth=""):
    rmi = RedisMemoryInfo(redis_host=redis_host, redis_port=redis_port, redis_auth=redis_auth)
    rmi.show_memory_info()


if __name__ == '__main__':
    fire.Fire(main)
